/* 
 * File:   Test.cpp
 * Author: user
 * 
 * Created on July 31, 2014, 3:18 AM
 */

#include <iostream>
#include <sstream>
#include <sqlite3.h>
#include "Test.h"

const string sql_DB = "CSCI222_A2.db";
const string ExamTopic_table = "ExamTopic";
const string Question_table = "Question";
const string TestSummary_table = "TestSummary";

//Static vatiable
static bool _header;
static bool _displayCallBack;

static bool _isSelectExamTopic;
static int _selectedExamTopicID;
static string _selectedExamTopic_topic;
static string _selectedExamTopic_desc;
static bool _examTopicFound;

static bool _isSelectQuestion;
static int _selectedTestQuestionID;
static bool _questionFound;

static int _totalMark;

static bool _testSummaryFound;

Test::Test() 
{
    _mark = 0;
}

Test::Test(const Test& orig) {
}

Test::~Test() {
}

string Test::getExamTopic()
{
    return _examTopic.getTopic();
}

bool Test::extractExamTopic(string createBy)
{
    bool ret(true);
    _displayCallBack = true;
    _examTopicFound = false;
    _header = true;
    char * sql;
    sql = sqlite3_mprintf("SELECT * FROM %q WHERE CreateBy='%q';",
            ExamTopic_table.c_str(), createBy.c_str()); 
    
    ret = executeSql(sql,ExamTopic_table);
    if(!_examTopicFound)
    {
        cout << endl << "No exam topic found in database!!!" << endl; 
        return false;
    }
  
    _displayCallBack = false;
    
    return true;
}

bool Test::promptUserForSelectingExamTopic()
{  
    _isSelectExamTopic = true;
    bool valid(false);
    string strTopicID;
    int iSelectedID;
    while(!valid)
    {
        cout << endl << "Please select a TopicID: ";
        getline(cin, strTopicID);

        valid = validateIntInput(strTopicID, iSelectedID);
    }
    
    char * sql;
    _examTopicFound = false;
    sql = sqlite3_mprintf("SELECT * FROM %q WHERE ID='%d';",
        ExamTopic_table.c_str(), iSelectedID); 
    
    executeSql(sql,ExamTopic_table);
    
    if(!_examTopicFound)
    {
        cout << endl << "No exam topic found in database!!!" << endl; 
        _isSelectExamTopic = false;
        return false;
    }
    
    //set selected exam topic and description
    updateExamObject();
    
    _isSelectExamTopic = false;
    return true;
}

bool Test::extractQuestion()
{
     bool ret(true);
    _displayCallBack = true;
    _questionFound = false;
    _header = true;
    //Reset mark;
    _totalMark = 0;
    
    char * sql;
    sql = sqlite3_mprintf("SELECT * FROM %q WHERE ExamTopic='%q';",
        Question_table.c_str(), _examTopic.getTopic().c_str()); 

    ret = executeSql(sql,Question_table);
    
    if(!_questionFound)
    {
        cout << endl << "No question found in database!!!" << endl; 
        _isSelectExamTopic = false;
        return false;
    }
    
    _displayCallBack = false;
    _header = false;
    
    _mark = _totalMark;
            
    cout << endl << "**********Result: " << _mark << " out of 5**********";
    
    return ret;
}

bool Test::updateTestToDB(string takenBy, string examTopic,struct tm* dateTime)
{
    //check whether user is first time take the test;
    _displayCallBack = false;
    _testSummaryFound = false;
    _header = false;
    char * sql;
    sql = sqlite3_mprintf("SELECT * FROM %q WHERE ExamTopic='%q' AND TakenBy='%q'",
            TestSummary_table.c_str(), examTopic.c_str(),takenBy.c_str()); 
    
    executeSql(sql,TestSummary_table);
    stringstream ss;
    ss << _mark << " out of 5";
    string result = ss.str();
    
    if(_testSummaryFound) //update TestSummary table
    {
        char * sqlUpdate;
        sqlUpdate = sqlite3_mprintf("UPDATE %q SET TimeOfAttempt = TimeOfAttempt +1, Result='%q', DateTime='%q' WHERE ExamTopic='%q' AND TakenBy='%q'",
            TestSummary_table.c_str(), result.c_str(), asctime(dateTime),examTopic.c_str(),takenBy.c_str()); 
    
        executeSql(sqlUpdate,TestSummary_table);
    }
    else
    {
        char * sqlInsert;
        sqlInsert = sqlite3_mprintf("INSERT INTO %q (ExamTopic,TakenBy,DateTime,Result,TimeOfAttempt) VALUES ('%q', '%q', '%q','%q','%d');" ,
                      TestSummary_table.c_str(), _examTopic.getTopic().c_str(), takenBy.c_str(),asctime(dateTime),result.c_str(),1);

        if (executeSql(sqlInsert,TestSummary_table))
            cout << endl << "Test result stored successfully!!!" << endl;
        else
        {
            cout << "Test result stored failed!!!" << endl;
            return false;
        }
    }
    return true;
}

bool Test::extractTestSummary(string takenBy)
{
    bool ret(true);
    _displayCallBack = true;
    _testSummaryFound = false;
    _header = true;
    char * sql;
    sql = sqlite3_mprintf("SELECT * FROM %q WHERE TakenBy='%q';",
            TestSummary_table.c_str(), takenBy.c_str()); 
    
    ret = executeSql(sql,TestSummary_table);
    if(!_testSummaryFound)
    {
        cout << endl << "No Test found in database!!!" << endl; 
        return false;
    }
  
    _displayCallBack = false;
    
    return true;
}

void Test::updateExamObject()
{
    _examTopic.setID(_selectedExamTopicID);
    _examTopic.setTopic(_selectedExamTopic_topic);
    _examTopic.setDesc(_selectedExamTopic_desc);
}

bool Test::validateIntInput(string input, int& output)
{
    for (int i=0;i<input.length();i++)
    {
        if (!isdigit(input[0])) 
        {
            cout << "Invalid input. Please try again" << endl;
            return false;
        }
    }

    stringstream ss (input);
    ss >> output;
    
    return true;
}

bool Test::executeSql(const char* sql,string table) 
{
   sqlite3 *db;
   char *zErrMsg = 0;
   int rc;
   /* Open database */
   rc = sqlite3_open(sql_DB.c_str(), &db); 
   
   if( rc )
   {
      fprintf(stderr, "\n\nCan't open database: %s\n", sqlite3_errmsg(db));
      return false;
   }else{
      //fprintf(stderr, "Opened database successfully\n");
   }
           
   /* Execute SQL statement */
   if (table == ExamTopic_table)
       rc = sqlite3_exec(db, sql, querryCallBack_ExamTopic, 0, &zErrMsg);  
   else if (table == Question_table)
       rc = sqlite3_exec(db, sql, querryCallBack_Question, 0, &zErrMsg);  
   else //Test summary
       rc = sqlite3_exec(db, sql, querryCallBack_TestSummary, 0, &zErrMsg); 
   
   if( rc != SQLITE_OK )
   {
      fprintf(stderr, "\n\nSQL error: %s\n", zErrMsg);
      sqlite3_free(zErrMsg);
      return false;
   }else{
      //fprintf(stdout, "retrieve data successfully\n");
   }
 
   sqlite3_close(db);
   return true;
   
}

int Test::querryCallBack_ExamTopic(void *data, int argc, char **argv, char **azColName)
{   
   if(_displayCallBack)
   {
        if (_header)
        {
         _header = false;
         cout << left << setw(10)
         << "TopicID" << left << setw(20)
         << "ExamTopic" << left << setw(80)
         << "Description";
   
#ifdef _debug
    cout << left << setw(20)
         << "CreateBy" << endl;
    cout << "----------------------------------------------------------------------------------------------------" << endl;
#else
    cout << endl;
    cout << "----------------------------------------------------------------------------------------------------" << endl;
#endif
        }
        cout << left << setw(10)
             << (argv[0] ? argv[0] : "NULL") << left << setw(20) 
             << (argv[1] ? argv[1] : "NULL") << left << setw(80) 
             << (argv[2] ? argv[2] : "NULL");
    
#ifdef _debug
    cout << left << setw(20)
         << (argv[3] ? argv[3] : "NULL") << endl;
#else
    cout << endl;
#endif

   }
   
   if (_isSelectExamTopic)
   {
       stringstream ss (argv[0]);
       ss >> _selectedExamTopicID;
    
       _selectedExamTopic_topic = (argv[1] ? argv[1] : "NULL");
       _selectedExamTopic_desc = (argv[2] ? argv[2] : "NULL");
   }
   _examTopicFound = true; 
   return 0;
}

int Test::querryCallBack_Question(void *data, int argc, char **argv, char **azColName)
{
   if (_displayCallBack)
   {
            cout << endl; 
            for(int i=1; i<argc-2; i++) //hide the first and last 2 columns(ID,Answer,CreateBy)
            { 
               if (i==1) // print header 
                    cout << "[-----Questions ID: " << (argv[0] ? argv[0] : "") << "-----]" << endl;  
               printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
            }
            
            processAnswer((argv[6] ? argv[6] : ""));
   }
   
   if (_isSelectQuestion)
   {
       stringstream ss (argv[0]);
       ss >> _selectedTestQuestionID;
    
   }
   _questionFound = true;
   return 0;
}

int Test::querryCallBack_TestSummary(void *data, int argc, char **argv, char **azColName)
{   
   if(_displayCallBack)
   {
        if (_header)
        {
            _header = false;
            cout << left << setw(30)
               << "ExamTopic" << left << setw(20)
               << "TakenBy" << left << setw(20)
               << "Result" << left << setw(15)
               << "TimeOfAttempt" << left << setw(20)
               << "DateTime" << endl;
            
            cout << "----------------------------------------------------------------------------------------------------" << endl;
        }
   
        cout << left << setw(30)
            << (argv[1] ? argv[1] : "NULL") << left << setw(20) 
            << (argv[2] ? argv[2] : "NULL") << left << setw(20) 
            << (argv[3] ? argv[3] : "NULL") << left << setw(15) 
            << (argv[4] ? argv[4] : "NULL") << left << setw(20) 
            << (argv[5] ? argv[5] : "NULL");
   }
  
   _testSummaryFound = true; 
   return 0;
}

void Test::processAnswer(string ans)
{ 
    bool valid(false);
    string userAns;
    while (!valid)
    {
        cout << "Please enter answer(a-d): ";
        getline(cin, userAns);
        
        if (userAns != "a" && userAns != "b" && 
            userAns != "c" && userAns != "d")
        {
            valid = false;
            cout << "Invalid input. Please try again." << endl;
        }
        else
            valid = true;
    }
   
    if (ans == userAns)
    {
        _totalMark++;
    }   
}
